Описание проекта


Вы работаете в интернет-магазине «Стримчик», который продаёт по всему миру компьютерные игры. Из открытых источников доступны исторические данные о продажах игр, оценки пользователей и экспертов, жанры и платформы (например, Xbox или PlayStation). Вам нужно выявить определяющие успешность игры закономерности. Это позволит сделать ставку на потенциально популярный продукт и спланировать рекламные кампании. Перед вами данные до 2016 года. Представим, что сейчас декабрь 2016 г., и вы планируете кампанию на 2017-й. Нужно отработать принцип работы с данными. Неважно, прогнозируете ли вы продажи на 2017 год по данным 2016-го или же 2027-й — по данным 2026 года. В наборе данных попадается аббревиатура ESRB (Entertainment Software Rating Board) — это ассоциация, определяющая возрастной рейтинг компьютерных игр. ESRB оценивает игровой контент и присваивает ему подходящую возрастную категорию, например, «Для взрослых», «Для детей младшего возраста» или «Для подростков».
In [1]:
import pandas as pd
import numpy as np
import math
import seaborn as sns
import plotly.express as px
import matplotlib.pyplot as plt
import plotly.io as pio
import plotly.graph_objs as go

from matplotlib import style
from plotly.offline import init_notebook_mode, iplot
from plotly.subplots import make_subplots
from scipy import stats as st
from scipy.stats import levene
from statsmodels.graphics.gofplots import qqplot
In [2]:
data_game = pd.read_csv('games.csv')

Подготовка данных¶

Проведем разведочный анализ данных

In [3]:
display(data_game.head(), data_game.tail())
Name Platform Year_of_Release Genre NA_sales EU_sales JP_sales Other_sales Critic_Score User_Score Rating
0 Wii Sports Wii 2006.0 Sports 41.36 28.96 3.77 8.45 76.0 8 E
1 Super Mario Bros. NES 1985.0 Platform 29.08 3.58 6.81 0.77 NaN NaN NaN
2 Mario Kart Wii Wii 2008.0 Racing 15.68 12.76 3.79 3.29 82.0 8.3 E
3 Wii Sports Resort Wii 2009.0 Sports 15.61 10.93 3.28 2.95 80.0 8 E
4 Pokemon Red/Pokemon Blue GB 1996.0 Role-Playing 11.27 8.89 10.22 1.00 NaN NaN NaN
Name Platform Year_of_Release Genre NA_sales EU_sales JP_sales Other_sales Critic_Score User_Score Rating
16710 Samurai Warriors: Sanada Maru PS3 2016.0 Action 0.00 0.00 0.01 0.0 NaN NaN NaN
16711 LMA Manager 2007 X360 2006.0 Sports 0.00 0.01 0.00 0.0 NaN NaN NaN
16712 Haitaka no Psychedelica PSV 2016.0 Adventure 0.00 0.00 0.01 0.0 NaN NaN NaN
16713 Spirits & Spells GBA 2003.0 Platform 0.01 0.00 0.00 0.0 NaN NaN NaN
16714 Winning Post 8 2016 PSV 2016.0 Simulation 0.00 0.00 0.01 0.0 NaN NaN NaN
In [4]:
data_game.shape
Out[4]:
(16715, 11)
In [5]:
data_game.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 16715 entries, 0 to 16714
Data columns (total 11 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   Name             16713 non-null  object 
 1   Platform         16715 non-null  object 
 2   Year_of_Release  16446 non-null  float64
 3   Genre            16713 non-null  object 
 4   NA_sales         16715 non-null  float64
 5   EU_sales         16715 non-null  float64
 6   JP_sales         16715 non-null  float64
 7   Other_sales      16715 non-null  float64
 8   Critic_Score     8137 non-null   float64
 9   User_Score       10014 non-null  object 
 10  Rating           9949 non-null   object 
dtypes: float64(6), object(5)
memory usage: 1.4+ MB
In [6]:
display(data_game.describe(),
        #описания категориальных признаков
        data_game.describe(include=[object]))
Year_of_Release NA_sales EU_sales JP_sales Other_sales Critic_Score
count 16446.000000 16715.000000 16715.000000 16715.000000 16715.000000 8137.000000
mean 2006.484616 0.263377 0.145060 0.077617 0.047342 68.967679
std 5.877050 0.813604 0.503339 0.308853 0.186731 13.938165
min 1980.000000 0.000000 0.000000 0.000000 0.000000 13.000000
25% 2003.000000 0.000000 0.000000 0.000000 0.000000 60.000000
50% 2007.000000 0.080000 0.020000 0.000000 0.010000 71.000000
75% 2010.000000 0.240000 0.110000 0.040000 0.030000 79.000000
max 2016.000000 41.360000 28.960000 10.220000 10.570000 98.000000
Name Platform Genre User_Score Rating
count 16713 16715 16713 10014 9949
unique 11559 31 12 96 8
top Need for Speed: Most Wanted PS2 Action tbd E
freq 12 2161 3369 2424 3990
In [7]:
data_game.isna().sum()
Out[7]:
Name                  2
Platform              0
Year_of_Release     269
Genre                 2
NA_sales              0
EU_sales              0
JP_sales              0
Other_sales           0
Critic_Score       8578
User_Score         6701
Rating             6766
dtype: int64
In [8]:
data_game.duplicated().sum()
Out[8]:
0

Промежуточный вывод:

  1. Видим большое количество пропусков: необходимо определить соответствующую причину пропусков в исходных данных и по мере возможности их восполнить
  2. Необходимо изменить тип данных колоннок: Year_of_Release и Critic_Score - на int, User_Score - на float
  3. Предлагаю переименовать и преобразовать к нижнему регистру колонки датафрейма
  4. Явные дубликаты невыявленны
In [9]:
data_game = data_game.rename(columns={'Year_of_Release':'year', 'NA_sales':'na', \
                                      'JP_sales':'jp', 'EU_sales':'eu', 'Other_sales':'other'})
data_game.columns = data_game.columns.str.lower()

Описание данных:

name — название игры
platform — платформа
year — год выпуска
genre — жанр игры
na — продажи в Северной Америке (миллионы проданных копий)
eu — продажи в Европе (миллионы проданных копий)
jp — продажи в Японии (миллионы проданных копий)
other — продажи в других странах (миллионы проданных копий)
critic_score — оценка критиков (максимум 100)
user_score — оценка пользователей (максимум 10)
rating — рейтинг от организации ESRB (англ. Entertainment Software Rating Board). Эта ассоциация определяет рейтинг компьютерных игр и присваивает им подходящую возрастную категорию.

При проверке типы данных, была удивлена колонке user_score - его формат 'object,
что очень странно, ведь если бы там были только числа и NaN'ы, то формат был бы 'float'

In [10]:
data_game.user_score.unique()
Out[10]:
array(['8', nan, '8.3', '8.5', '6.6', '8.4', '8.6', '7.7', '6.3', '7.4',
       '8.2', '9', '7.9', '8.1', '8.7', '7.1', '3.4', '5.3', '4.8', '3.2',
       '8.9', '6.4', '7.8', '7.5', '2.6', '7.2', '9.2', '7', '7.3', '4.3',
       '7.6', '5.7', '5', '9.1', '6.5', 'tbd', '8.8', '6.9', '9.4', '6.8',
       '6.1', '6.7', '5.4', '4', '4.9', '4.5', '9.3', '6.2', '4.2', '6',
       '3.7', '4.1', '5.8', '5.6', '5.5', '4.4', '4.6', '5.9', '3.9',
       '3.1', '2.9', '5.2', '3.3', '4.7', '5.1', '3.5', '2.5', '1.9', '3',
       '2.7', '2.2', '2', '9.5', '2.1', '3.6', '2.8', '1.8', '3.8', '0',
       '1.6', '9.6', '2.4', '1.7', '1.1', '0.3', '1.5', '0.7', '1.2',
       '2.3', '0.5', '1.3', '0.2', '0.6', '1.4', '0.9', '1', '9.7'],
      dtype=object)
In [11]:
# Загуглив значение tbd - было выясненно - это to be determined, что в переводе "предстоит определить"
data_game.query('user_score == "tbd"').shape
Out[11]:
(2424, 11)

Комментарий:
Данных с соответствующим значением, как мы видим большое количество.
Предлагаю заменить их пустым значением типа NAN для проведения дальнейшего анализа данных

In [12]:
data_game.user_score = data_game.user_score.replace('tbd', np.nan).astype('float')
data_game.query('user_score == "tbd"').shape
Out[12]:
(0, 11)
In [13]:
data_game.year = data_game.year.astype('Int64')
data_game.critic_score = data_game.critic_score.astype('Int64')

Обработка пропущенных значений

In [14]:
# Начнем по-порядку. В колонке [name] всего два пропуска, проверим...
data_game.query('name != name')
Out[14]:
name platform year genre na eu jp other critic_score user_score rating
659 NaN GEN 1993 NaN 1.78 0.53 0.00 0.08 <NA> NaN NaN
14244 NaN GEN 1993 NaN 0.00 0.00 0.03 0.00 <NA> NaN NaN

Что касается колонки year, пропусков - 269, пропуски невосполнить средним либо медианным значением... Предлагаю провести маленький экспиремент, отобрать три игры и посмотреть год релиза для разных платформ и на основе результатов по аналогии восполнить все пропущенные значения указанным годом на одной из платформ

In [15]:
data_game.query('year.isnull()')['name'].reset_index().head(3)
Out[15]:
index name
0 183 Madden NFL 2004
1 377 FIFA Soccer 2004
2 456 LEGO Batman: The Videogame
In [16]:
# Красным подмечено пустые значения
display(data_game.query('name == "Madden NFL 2004"').style.highlight_null('red'), 
        data_game.query('name == "FIFA Soccer 2004"').style.highlight_null('red'), 
        data_game.query('name == "LEGO Batman: The Videogame"').style.highlight_null('red'))
  name platform year genre na eu jp other critic_score user_score rating
183 Madden NFL 2004 PS2 Sports 4.260000 0.260000 0.010000 0.710000 94 8.500000 E
1881 Madden NFL 2004 XB 2003 Sports 1.020000 0.020000 0.000000 0.050000 92 8.300000 E
3889 Madden NFL 2004 GC 2003 Sports 0.400000 0.100000 0.000000 0.010000 94 7.700000 E
5708 Madden NFL 2004 GBA 2003 Sports 0.220000 0.080000 0.000000 0.010000 70 6.600000 E
  name platform year genre na eu jp other critic_score user_score rating
377 FIFA Soccer 2004 PS2 Sports 0.590000 2.360000 0.040000 0.510000 84 6.400000 E
2606 FIFA Soccer 2004 XB 2003 Sports 0.240000 0.490000 0.000000 0.050000 82 8.200000 E
12029 FIFA Soccer 2004 GC 2003 Sports 0.050000 0.010000 0.000000 0.000000 83 6.200000 E
13086 FIFA Soccer 2004 GBA 2003 Sports 0.040000 0.010000 0.000000 0.000000 82 7.900000 E
  name platform year genre na eu jp other critic_score user_score rating
397 LEGO Batman: The Videogame X360 2008 Action 2.040000 1.020000 0.000000 0.320000 76 7.900000 E10+
456 LEGO Batman: The Videogame Wii Action 1.800000 0.970000 0.000000 0.290000 74 7.900000 E10+
460 LEGO Batman: The Videogame DS 2008 Action 1.750000 1.010000 0.000000 0.290000 72 8.000000 E10+
1519 LEGO Batman: The Videogame PS3 2008 Action 0.720000 0.390000 0.000000 0.190000 75 7.700000 E10+
1538 LEGO Batman: The Videogame PSP Action 0.570000 0.440000 0.000000 0.270000 73 7.400000 E10+
1553 LEGO Batman: The Videogame PS2 2008 Action 0.720000 0.030000 0.000000 0.520000 77 8.900000 E10+
12465 LEGO Batman: The Videogame PC 2008 Action 0.020000 0.030000 0.000000 0.010000 80 7.800000 E10+
In [17]:
def fill_year (data_game, year, name):
    for index in data_game[name].unique():
        data_game.loc[(data_game[year].isna())&(data_game[name] == index), year] = \
        data_game.loc[data_game[name] == index, year].max()
    return data_game

fill_year(data_game, 'year', 'name')
print(f'Пропущенных значений в колонке year - {data_game.year.isna().sum()}, что составляет - \
{round(data_game.year.isna().mean()*100, 2)}%')
Пропущенных значений в колонке year - 146, что составляет - 0.87%
In [18]:
display(data_game.query('year.isnull()')['name'].reset_index().head(2), data_game.query('name == "Rock Band"'))
index name
0 475 wwe Smackdown vs. Raw 2006
1 627 Rock Band
name platform year genre na eu jp other critic_score user_score rating
627 Rock Band X360 <NA> Misc 1.93 0.33 0.0 0.21 92 8.2 T
805 Rock Band Wii <NA> Misc 1.33 0.56 0.0 0.20 80 6.3 T
1142 Rock Band PS3 <NA> Misc 0.99 0.41 0.0 0.22 92 8.4 T
1840 Rock Band PS2 <NA> Misc 0.71 0.06 0.0 0.35 82 6.8 T
In [19]:
# Предлагаю удалить все строки с пропусками в name, year и genre.
data_game.dropna(subset=['name', 'year', 'genre'], inplace=True)

Комментарий:
Поскольку количество пропусков в колонках critic_score и user_score очень большое и причины пропусков могут быть обусловлены разными факторами, предлагаю восполнить их на технические индикаторы для возможности выделения при анализе: оценки критиков и пользователей - на 0, т.к. столбцы имеют колличественные данные.

In [20]:
data_game.critic_score = data_game.critic_score.fillna(0)
data_game.user_score = data_game.user_score.fillna(0)
In [21]:
# Просмотрим уникальные значения колонки [rating]
data_game.rating.unique()
Out[21]:
array(['E', nan, 'M', 'T', 'E10+', 'K-A', 'AO', 'EC', 'RP'], dtype=object)

Комментарий:
Столбец rating содержит буквенное обозначение рейтинга, присвоенного видеоигре организацией ESRB (всего 8 уникальных значений + пропуски).

Справочная информация (источник - https://ru.wikipedia.org/wiki/Entertainment_Software_Rating_Board): Entertainment Software Rating Board (ESRB) — негосударственная организация, основное направление деятельности — принятие и определение рейтингов для компьютерных видеоигр и другого развлекательного программного обеспечения в США и Канаде.

Расшифровка буквенных обозначений рейтинга:
«EC» («Early childhood») — «Для детей младшего возраста»
«E» («Everyone») — «Для всех» (до 1998 года - "K-A" ("Kids to Adults"))
«E10+»(«Everyone 10 and older») — «Для всех от 10 лет и старше»
«T» («Teen») — «Подросткам»
«M»(«Mature») — «Для взрослых»
«AO» («Adults Only 18+») — «Только для взрослых»
«RP» («Rating Pending») — «Рейтинг ожидается»

В других странах существуют свои системы стандартизации и лицензирования видеоигр (например, как PEGI в Европейском Союзе, CERA в Японии). Отсутствие рейтинга в датасете может обозначать, что игра была произведена за переделами Северной Америки и не подлежала оценке ESRB. Предлагаю восполнить пропущенные значения на «Indefinite» — «Неопределенно»

In [22]:
data_game.rating = data_game.rating.fillna('Indefinite')
In [23]:
# Запишим в отдельную колонку сумму всех продаж по всем регионам
data_game['total_sales'] = data_game[['na','eu','jp','other']].sum(axis = 1)

Промежуточный вывод:

Все выявленные замечания были устранены: избавились от пропусков, изменили тип данных, убедились в отсутствии дубликатов, привели названия столбцов к удобному виду.

Исследовательский анализ данных¶

Посмотрите, сколько игр выпускалось в разные годы. Важны ли данные за все периоды?

In [24]:
def create_any_bar(groupby_column, func, y='name'):
    plt.style.use('seaborn-ticks')
    game_plot = data_game.groupby(groupby_column)[y]
    if func == 'count':
        game_plot_calculated = game_plot.count()
        figsize = (15,5)
        plt.ylabel('Количество в млн. шт.')
        plot = game_plot_calculated.plot(kind='bar', y=y, figsize=figsize, ec='black', title = 'Количество продаж по годам')
    elif func == 'sum':
        game_plot_calculated = game_plot.sum().sort_values()
        figsize = (15,10)
        plt.xlabel('Количество в млн. шт.')
        plot = game_plot_calculated.plot(kind='barh', y=y, figsize=figsize, ec='black', title = 'Сумма продаж по платформам')
        
In [25]:
create_any_bar('year', 'count')
In [26]:
# Агрегируем данные и посмотрим на динамику продаж в макрорегиионах
df = data_game.groupby('year', as_index=False).agg({'na':'sum', 'eu':'sum', 'jp':'sum', 'other':'sum'})
fig = px.line(df, x='year', y=['na', 'eu', 'jp', 'other'], template='plotly_white', title='Динамика продаж по макрорегионам')
fig.show()

Комментарий:

Видно, что до 1990 года количество игр было малым. 1991г. - негласно можно считать точкой отсчета в развитии и распространеннии видеоигр - это может быть связано с бурным технологическим развитичем. Резкий скачок по количеству игр, выпущенных в год, произошел в 2002 году. Пик - 2008-2009 года. После этого начался спад, связанный с развитием мобильных устройств, интеграция соц. сетей в жизнь людей. Это всё привело к тому, что интерес к самим играм упал, соответственно, упало и их производство. Также из графика Динамика продаж по макрорегионам - становится ясно, какой макрорегион создает тренды.

Интересная статья - https://www.gamesindustry.biz/gamesindustry-biz-presents-the-year-in-numbers-2019

Посмотрите, как менялись продажи по платформам. Выберите платформы с наибольшими суммарными продажами и постройте распределение по годам. За какой характерный срок появляются новые и исчезают старые платформы?

In [27]:
create_any_bar('platform', 'sum', 'total_sales')

Комментарий:

Лично я знакома далеко не с всеми платформами в списке, но конечно же, было очевидным, что лидером продаж является платформа PS2 - она имела популярность и была как минимум у каждого на слуху. Далее идут Xbox360, PS3 и Wii ....

Возьмите данные за соответствующий актуальный период. Актуальный период определите самостоятельно в результате исследования предыдущих вопросов. Основной фактор — эти данные помогут построить прогноз на 2017 год.

In [28]:
# Произведем расчет общих продаж: построим сводную таблицу по платформам
data_platforms = data_game.pivot_table(index=['platform'],
                                       values= 'total_sales',
                                       aggfunc='sum') \
                          .sort_values(by='total_sales', ascending =False).reset_index()

display(data_platforms.head(15).style.background_gradient(sns.light_palette("brown", as_cmap=True)))
  platform total_sales
0 PS2 1247.160000
1 X360 966.610000
2 PS3 935.190000
3 Wii 903.310000
4 DS 804.280000
5 PS 727.580000
6 PS4 314.140000
7 GBA 313.730000
8 PSP 293.570000
9 PC 258.860000
10 3DS 258.530000
11 XB 256.690000
12 GB 254.430000
13 NES 251.050000
14 N64 218.480000
In [29]:
''' Проанализируем данные начала 21-го века 5-лидирующих платформ: с периода 2000 по 2016 г.г.
    Именно с этого момента начинается уверенный рост рынка видеоигр '''

data_2000 = data_game[data_game['year'] >= 2000]

''' Посмотрим, как менялись продажи по платформам. Выберем платформы с наибольшими суммарными продажами и 
построим распределение по годам. Ответим на вопрос: за какой характерный срок появляются новые и исчезают
старые платформы? '''

# Возьмем индексы и преобразуем их в лист
platforms = data_2000.groupby('platform')['total_sales'].sum().sort_values(ascending=False)[:5].index.tolist()
data = []

for index in platforms:
    data.append(go.Bar(x=data_2000[data_2000.platform == index].groupby("year")['total_sales'].sum().index,
                       y=data_2000[data_2000.platform == index].groupby("year")['total_sales'].sum(), name=index))
    
layout = {'title': 'Продажи по годам'}
fig = go.Figure(data=data, layout=layout)
fig.layout.template = 'plotly_white'
fig.update_layout(legend_title_text = "Платформа")
fig.update_xaxes(title_text="Год")
fig.update_yaxes(title_text="Уровень продаж")
fig.show()

Промежуточный вывод:

Определенно можно заявить, что практически все игровые платформы в среднем существуют до 10 лет включительно. Примерный пик "Популярноси | Продаваемости" ≈ наступает через 5 лет после релиза. Самые успешные из них это Sony Play Station и XBOX, у Nintento WII был быстрый скачок в 2009 году и такой же резкий провал

Таким образом, в среднем консоль живет 10 лет. Это неизбежно, т.к. прогресс идет вперед и "железо" в старых консолях перестает отвечать техническим требованиям новых игр.

Нам необходимо выбрать актуальный период - период, за который мы будем оценивать наши данные, на основании выводов по которым будем строить предсказательные модели на 2017 год. Многие новые платформы, на тот период, появляются в 2006 году (например: Wii, PS3, X360), при этом характерный срок жизни - это 10 лет, но к 2013 году мы видим их активный спад, к тому видим рост актуальных на нынешнее время платформ, их в дальнейшем мы и будем разбирать. Для целей прогнозирования продаж на следующий год в динамично меняющейся индустрии, как компьютерные игры, предлагаю охватит данные с 2013 года.

In [30]:
data_2013 = data_game[data_game['year'] >= 2013]

Какие платформы лидируют по продажам, растут или падают? Выберите несколько потенциально прибыльных платформ.

In [31]:
data = []

for index in data_2013.platform.unique():
    data.append(go.Bar(x=data_2013[data_2013.platform == index].groupby("year")['total_sales'].sum().index,
                       y=data_2013[data_2013.platform == index].groupby("year")['total_sales'].sum(), name=index))

    
layout = {'title': 'Продажи по годам'} 

fig = go.Figure(data=data, layout=layout)
fig.layout.template = 'plotly_white'

fig.update_layout(legend_title_text = "Платформа")
fig.update_xaxes(title_text="Год")
fig.update_yaxes(title_text="Уровень продаж")
fig.show()

Комментарий:

Видим общую для всех платформ тенденцию снижения продаж, явными лидерами к 2016г. являются платформы PS4, XOne и 3DS

Постройте график «ящик с усами» по глобальным продажам игр в разбивке по платформам. Опишите результат.

In [32]:
#Выделим шесть топовых платформ с которыми мы будем дальше работать
data_box = data_2013.query('platform in ("PS4", "XOne", "PC", "WiiU", "3DS", "PSV")') \
                    .pivot_table(index=['name', 'platform', 'year'], 
                                 values='total_sales').sort_values(by='platform').reset_index()
data_box.head(10)
Out[32]:
name platform year total_sales
0 Beyblade Burst 3DS 2016 0.03
1 Rodea the Sky Soldier 3DS 2015 0.03
2 Romance of the Three Kingdoms (3DS) 3DS 2013 0.03
3 Romance of the Three Kingdoms II 3DS 2015 0.03
4 Dream Girl Premier 3DS 2015 0.03
5 Dragon Quest X 3DS 2014 0.17
6 Dragon Quest VIII: Journey of the Cursed King 3DS 2015 0.86
7 Dragon Quest VII: Warriors of Eden 3DS 2013 1.46
8 Dragon Quest Monsters Joker 3 3DS 2016 0.63
9 Dragon Quest Monsters 2 3DS 2014 0.79
In [33]:
data = []

for index in data_box.platform.unique():
    data.append(go.Box(y=data_box[data_box.platform == index]['total_sales'], name=index ))
    
layout = {'title': 'Продажи по платформам'}
fig = go.Figure(data=data, layout=layout)

fig.update_layout(yaxis=dict(range=[0,1])) # Изменим масштаб по оси y
fig.layout.template = 'plotly_white'

# Тут можем поиграться боксплотом
iplot(fig, show_link = True)

Промежуточный вывод:
Что сразу же бросается в глаза - это разное максимальное и медианное значение продаж у платформ. У явного лидера - PS4 - распределение смещено в сторону максимальных значений и это самая популярная, и успешная платформа: игры, созданные для нее, продавались большими тиражами.

Посмотрите, как влияют на продажи внутри одной популярной платформы отзывы пользователей и критиков. Постройте диаграмму рассеяния и посчитайте корреляцию между отзывами и продажами. Сформулируйте выводы.

In [34]:
# Выбираю PS4 - это самая популярная, и успешная платформа
sns.pairplot(data_game[data_game.platform == "PS4"][['total_sales', 'critic_score', 'user_score']])
plt.show()
/home/jupyter-zurab/.local/lib/python3.9/site-packages/seaborn/distributions.py:499: FutureWarning:

In a future version, the Index constructor will not infer numeric dtypes when passed object-dtype sequences (matching Series behavior)

/home/jupyter-zurab/.local/lib/python3.9/site-packages/seaborn/distributions.py:500: FutureWarning:

In a future version, the Index constructor will not infer numeric dtypes when passed object-dtype sequences (matching Series behavior)

Комментарий:
Видим нулевые выбросы - это пропущенные значения, где мы условились восполнить их нулями.

In [35]:
# Применим корреляцию Спирмена, чтобы отбросить нулевые выбросы
data_game[data_game.platform == "PS4"][['total_sales', 'critic_score', 'user_score']].corr(method='spearman')
Out[35]:
total_sales critic_score user_score
total_sales 1.000000 0.414235 0.175916
critic_score 0.414235 1.000000 0.797199
user_score 0.175916 0.797199 1.000000

Промежуточный вывод:
В целом видим слабую прямую взаимосвязь, или иными словами, продажи не зависят от оценок критиков и пользователей, но к критикам прислушиваются больше.

Соотнесите выводы с продажами игр на других платформах.

In [36]:
other_platforms = ['PS4','XOne','PC','WiiU','3DS', 'PSV']

rows = 3
cols = 2
fig, axes = plt.subplots(rows, cols, figsize=(15,10))

# Устанавливаем счетчик, для перебора платформ в списке через индекс
count = 0
for a in range(rows):
    for b in range(cols):
        # перебираем по индексу
        index = other_platforms[count]
        df = data_game[(data_game['platform'] == index) & (data_game['user_score'] > 0)]
        
        df_filtered = df[['total_sales' ,'critic_score', 'user_score']]
        sns.set(font_scale=1.0)
        ax = sns.heatmap(df_filtered.corr()[['total_sales']].sort_values(by='total_sales', ascending=False), 
                         cmap="Blues", annot=True, annot_kws={'size':15}, ax=axes[a,b])
        ax.set_title(index, fontsize=20)
        ax.set_yticklabels(ax.get_yticklabels(), rotation=0)
        plt.tight_layout(pad=5)
        count += 1

Промежуточный вывод:
У всех 6 платформ наблюдается не сильная связь между оценками критиков и продажами. Cвязь между оценками пользователей и продажами слабая у всех платформ, либо её совсем нет. У WiiU корреляция между отзывами пользователей и продажами - уверенная и даже выше, чем корреляция между отзывами критиков и игровой платформой.

Посмотрите на общее распределение игр по жанрам. Что можно сказать о самых прибыльных жанрах? Выделяются ли жанры с высокими и низкими продажами?

In [37]:
ax = plt.gca()
ratio_genres = data_2013.groupby('genre') \
                        .agg({'name': 'count', 'total_sales': 'sum'}) \
                        .sort_values(by='name', ascending=False)
games = ratio_genres['name']
games.plot(kind='bar', figsize=(15,5), ec='black', ax=ax, width=0.2, position=1)

sales = ratio_genres['total_sales']
sales.plot(kind='bar', figsize=(15,5), ec='black', ax=ax, width=0.2, color='#97F0AA', position=0)

ax.legend(['Количество продаж', 'Общая сумма продаж'])

ratio_genres['ratio'] = ratio_genres['total_sales'] / ratio_genres['name']
ratio_genres.sort_values(by='ratio', ascending=False).reset_index() \
.style.background_gradient(sns.light_palette("brown", as_cmap=True))
Out[37]:
  genre name total_sales ratio
0 Shooter 187 232.980000 1.245882
1 Sports 214 150.650000 0.703972
2 Platform 74 42.630000 0.576081
3 Role-Playing 292 145.890000 0.499623
4 Racing 85 39.890000 0.469294
5 Fighting 80 35.310000 0.441375
6 Action 769 322.500000 0.419376
7 Misc 156 63.060000 0.404231
8 Simulation 62 21.760000 0.350968
9 Puzzle 17 3.170000 0.186471
10 Strategy 56 10.080000 0.180000
11 Adventure 245 23.640000 0.096490
In [38]:
# Построим боксплот
data_box = data_2013.query('platform in ("PS4", "XOne", "PC", "WiiU", "3DS", "PSV")') \
                    .pivot_table(index=['name', 'platform', 'genre'], 
                                 values='total_sales').sort_values(by='platform').reset_index()
data = []

for index in data_box.genre.unique():
    data.append(go.Box(y=data_box[data_box.genre == index]['total_sales'], name=index ))
    
layout = {'title': 'Продажи по жанрам'}
fig = go.Figure(data=data, layout=layout)

fig.update_layout(yaxis=dict(range=[0,2])) # Изменим масштаб по оси y
fig.layout.template = 'plotly_white'

iplot(fig, show_link = False)

Промежуточный вывод:

Самый выпускаемый жанр - Action - это связано с широким спектром данного жанра, в том смысле, что жанр представлен во множестве разновидностей от файтингов, шутеров и платформеров, сам по осебе жанр много в себя вбирает

Самый прибыльный жанр - Shooter - жанр никогда себя не изживет и всегда актуален

Жанр Sports стоит на третьем месте по продажам - (Серии FIFA, PES и пр.)

Неожиданно, что Adventure сильно отстаёт по продажам, он схож по уровню с жанром Simulation хотя игр выпускается много - (Довольно нишевый жанр, как правилило совмещенный с другими жанрами, а иначе очень быстро надоедает)

Итоги:

  • Самый прибылный жанр - Shooter
  • Самый не прибыльный - Adventure

Вывод по третьему блоку:
При исследовании данных мы рассмотрели продажи игр взависиомти по годам, определили среднюю продолжнительность жизни платформы, определили 6 потенциально прибыльные платформы, рассмотрели их корреляцию с рейтингом пользователей и экспертов. Рассмотрели продажи игр в зависимости от жанра. Обобщив результаты можем сказать, что потенциально прибыльные игры должны быть на платформах - XOne, PS4, 3DS, с жанром Shooter, Sports и Platform с любыми рейтингами пользователей или экспертов. Мы определили общий портрет игры с наибольшими продажами. Теперь определим портрет пользователя для каждого региона.

Составьте портрет пользователя каждого региона¶

Самые популярные платформы (топ-5). Опишите различия в долях продаж

In [39]:
# Группировка данных ТОП5 платформ по продажам в разрезе рынков
na = data_game[data_game.year > 2012].groupby('platform')['na'].agg(na='sum').nlargest(5, 'na').reset_index()
eu = data_game[data_game.year > 2012].groupby('platform')['eu'].agg(eu='sum').nlargest(5, 'eu').reset_index()
jp = data_game[data_game.year > 2012].groupby('platform')['jp'].agg(jp='sum').nlargest(5, 'jp').reset_index()
In [40]:
# График продаж по платформам в разрезе рынков
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle('Продажи по платформам в разрезе рынков')

sns.barplot(y='na', x='platform', data=na, ax=ax[0], palette='pastel')
ax[0].set_title('Северная Америка')
ax[0].set_ylabel('продажи, млн.шт.')
ax[0].set_xlabel('платформа')

sns.barplot(y='eu', x='platform', data=eu, ax=ax[1], palette='pastel')
ax[1].set_title('Европа')
ax[1].set_ylabel('продажи, млн.шт.')
ax[1].set_xlabel('платформа')

sns.barplot(y='jp', x='platform', data=jp, ax=ax[2], palette='pastel')
ax[2].set_title('Япония')
ax[2].set_ylabel('продажи, млн.шт.')
ax[2].set_xlabel('платформа')
plt.show()

Комментарий:
В Северной Америке и в Европе лидер - PS4, а в Японии - мобильный 3DS, Япония - последний оплот компании Nintendo, 3DS и WiiU в сумме занимают 76% местного рынка. XOne от Microsoft второй в Северной Америке, в Японии его доля - статистическая погрешность. В Европе кроме лидирущего с большим отрывом PS4 остальные платформы показывают почти одинаковые цифры.

Самые популярные жанры (топ-5). Поясните разницу.

In [41]:
# Группировка данных ТОП5 жанров по продажам в разрезе рынков
na_genres = data_game[data_game.year > 2012].groupby('genre')['na'].agg(na='sum').nlargest(5, 'na').reset_index()
eu_genres = data_game[data_game.year > 2012].groupby('genre')['eu'].agg(eu='sum').nlargest(5, 'eu').reset_index()
jp_genres = data_game[data_game.year > 2012].groupby('genre')['jp'].agg(jp='sum').nlargest(5, 'jp').reset_index()
In [42]:
# График продаж по жанрам в разрезе рынков
fig, ax = plt.subplots(1, 3, figsize=(20, 5))
fig.suptitle('Продажи по жанрам в разрезе рынков')

sns.barplot(y='na', x='genre', data=na_genres, ax=ax[0], palette='pastel')
ax[0].set_title('Северная Америка')
ax[0].set_ylabel('продажи, млн.шт.')
ax[0].set_xlabel('жанр')

sns.barplot(y='eu', x='genre', data=eu_genres, ax=ax[1], palette='pastel')
ax[1].set_title('Европа')
ax[1].set_ylabel('продажи, млн.шт.')
ax[1].set_xlabel('жанр')

sns.barplot(y='jp', x='genre', data=jp_genres, ax=ax[2], palette='pastel')
ax[2].set_title('Япония')
ax[2].set_ylabel('продажи, млн.шт.')
ax[2].set_xlabel('жанр')
plt.show()

Комментарий:
В Северной Америке и в Европе больше всего продаются игры жанра Action, затем Shooter, потом Sports
Разница между Западом и Востоком очевидна, в Японии другой лидер - жанр Role-Playing, Action идут с большим отрывом вторыми, а вот Shooter - не так популярен

Влияние рейтинга ESRB на продажи по регионам

In [43]:
na_rating = data_game[data_game.year > 2013].groupby('rating')['na'].agg(na_sales='sum').reset_index()
eu_rating = data_game[data_game.year > 2013].groupby('rating')['eu'].agg(eu_sales='sum').reset_index()
jp_rating = data_game[data_game.year > 2013].groupby('rating')['jp'].agg(jp_sales='sum').reset_index()
In [44]:
fig, ax = plt.subplots(1,3, figsize=(20, 6))
fig.suptitle('Продажи по рейтингам ESRB в разрезе рынков')
data1, categories1 = na_rating['na_sales'], na_rating['rating']
data2, categories2 = eu_rating['eu_sales'], eu_rating['rating']
data3, categories3 = jp_rating['jp_sales'], jp_rating['rating']
colors = ['#006699','#009933','#FF9900','#996600', '#CC0000']

def func(pct, allvals):
    absolute = int(pct/100.*np.sum(allvals))
    return "{:.1f}% ({:d} млн )".format(pct, absolute)

wedges, texts, autotexts = ax[0].pie(data1 ,autopct=lambda pct: func(pct, data1), textprops=dict(color="w"), colors=colors)
ax[0].set_title("Северная Америка")
ax[0].legend(wedges, categories1, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)

wedges, texts, autotexts = ax[1].pie(data2 ,autopct=lambda pct: func(pct, data2), textprops=dict(color="w"),colors=colors)
ax[1].set_title("Европа")
ax[1].legend(wedges, categories2, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)

wedges, texts, autotexts = ax[2].pie(data3 ,autopct=lambda pct: func(pct, data3), startangle = 65, textprops=dict(color="w"),colors=colors)
ax[2].set_title("Япония")
ax[2].legend(wedges, categories3, title="рейтинг", loc="upper right")
plt.setp(autotexts, size=8, weight=850)
plt.show()    

Расшифровка буквенных обозначений рейтинга:
«EC» («Early childhood») — «Для детей младшего возраста»
«E» («Everyone») — «Для всех» (до 1998 года - "K-A" ("Kids to Adults"))
«E10+»(«Everyone 10 and older») — «Для всех от 10 лет и старше»
«T» («Teen») — «Подросткам»
«M»(«Mature») — «Для взрослых»
«AO» («Adults Only 18+») — «Только для взрослых»
«RP» («Rating Pending») — «Рейтинг ожидается»
«Indefinite» — «Неопределенно»

Промежуточный вывод:
Распределение рейтинга в Северной Америке и в Европе очень схожа - треть игр имеют рейтинг «M», пятая часть - рейтинг «Е», четверть - «Е10+» рейтинги, у примерно четверти игр рейтинг отсутствует.

В Японии с учетом восточной психо-национальной особенности, лидером в рейтинге является T — «Подросткам» - 15.9%, рейтинг «Е» - 9.6% и совсем малую часть занимают рейтинги «M» и «Е10+» В Японии рейтинг отсутствует более чем у половины игр, большое количество отсутствия рейтингов в Японии возможно объяснить наличием национальной рейтинговой организации, что справедливо и для Европы, где есть как общие так и национальные организации. Онлайн магазины тип Stream вообще могут не получать рейтинги, или пользоватся международной IARC для полностью цифровых продаж.

Вывод по четвертому блоку: «Портрет пользователя определенного рынка»
Пользователь из Северной Америки любит и покупает Action в пяти случаях из ста, чуть меньше - Shooter. В основном у него игры с рейтингом M и E. Практически у каждого второго приставка Play Station 4, у каждого третьего XOne от Microsoft.

Пользователь из Европы однозначно почти в половине случаев выбирает игры для Play Station 4, XOne у него окажется с такой же вероятностью что и игровой ноутбук или мобильная 3DS - в 13-16 случаях из ста. В остальном его предпочтения такие же как у пользователя из Северной Америки - больше всего любит Action, затем Shooter, в половине случаев с рейтингом M и E

Пользователи из Японии уникальны, как и их страна. Они не замечают и не притрагиваются к XOne от Microsoft, Свежий PS4 У японца можно встретиь в десяти случаях из ста. В отличии от старого доброго 3DS от Nintendo, игры для которой забирают 66% продаж в индустрии в последние годы. Японец не любит "стрелялки-шутеры", они его почти не интересуют, зато ролевые игры у него на первом месте, единственное что роднит его в предпочтениях с американцем и европейцем - популяность жанра Action.

Проверьте гипотезы¶

Гипотеза № 1: Средние пользовательские рейтинги платформ Xbox One и PC одинаковые

Применим t-критерий Стьюдента для подтверждения гипотезы о равенстве средних двух генеральных совокупностей Определим пороговое значение p-value = 0.05

Н₀ (нулевая гипотеза) - средние пользовательские рейтинги платформ Xbox One и PC одинаковые.

Н₁(альтернативная гипотеза) - средние пользовательские рейтинги платформ Xbox One и PC отличаются между собой.

In [45]:
sample_xone = data_game[data_game.year > 2013].query('platform == "XOne" & user_score != 0')['user_score'] # Оценки пользователей Xbox One
sample_pc = data_game[data_game.year > 2013].query('platform == "PC" & user_score != 0')['user_score']
print('Размер выборки Xbox One:',len(sample_xone))
print('Размер выборки PC:',len(sample_pc))
Размер выборки Xbox One: 165
Размер выборки PC: 123
In [46]:
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Распределение значений оценки пользователей')

sns.histplot(sample_xone, ax=axes[0,0], kde=False, bins=25)
axes[0,0].set_title('XOne')
axes[0,0].set_xlabel('Оценка')
axes[0,0].set_ylabel('Частота')

sns.histplot(sample_pc, ax=axes[0,1], kde=False, bins=25)
axes[0,1].set_title('PC')
axes[0,1].set_xlabel('Оценка')
axes[0,1].set_ylabel('Частота')

# “s” - стандартизированная строка, ожидаемая статистика заказов масштабируется на стандартное
# отклонение данной выборки и к ним добавляется среднее значение
qqplot(sample_xone, line='s', ax=axes[1,0])
qqplot(sample_pc, line='s', ax=axes[1,1])
plt.show()
In [47]:
alpha = 0.05 # критический уровень статистической значимости
            # если p-value окажется меньше него - отвергнем гипотезу

results_1 = st.ttest_ind(
    sample_xone, 
    sample_pc,
    equal_var = False)

print('p-значение:', results_1.pvalue)

if (results_1.pvalue < alpha):
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
p-значение: 0.139237120435483
Не получилось отвергнуть нулевую гипотезу

Вывод по гипотеза № 1:
На имеющихся данных, на уровне значимости 5% (уровне доверия 95%) нет оснований отвергнуть нулевую гипотезу о равенстве средних значений оценок пользователей платформ XOne и PC в пользу альтернативной.
Насколько я поняла XOne и PC имеют почти одинаковый набор игр, как и сам сервис [Microsoft], который работает и на PC, и на XOne, следовательно рейтинг у них будет одинаковый.

Гипотеза № 2: Средние пользовательские рейтинги жанров Action и Sports разные

Применим t-критерий Стьюдента для подтверждения гипотезы о равенстве средних двух генеральных совокупностей
Определим пороговое значение p-value = 0.05

Н₀ (нулевая гипотеза) - средние пользовательские рейтинги платформ Action и Sports одинаковые.

Н₁(альтернативная гипотеза) - средние пользовательские рейтинги платформ Action и Sports отличаются между собой.

In [48]:
sample_action = data_game[data_game.year > 2013].query('genre == "Action" & user_score != 0')['user_score']
sample_sports = data_game[data_game.year > 2013].query('genre == "Sports" & user_score != 0')['user_score']
print('Размер выборки Action:',len(sample_action))
print('Размер выборки Sports:',len(sample_sports))
Размер выборки Action: 298
Размер выборки Sports: 127
In [49]:
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Распределение значений оценки пользователей')

sns.histplot(sample_action, ax=axes[0,0], kde=False, bins=25)
axes[0,0].set_title('Action')
axes[0,0].set_xlabel('Оценка')
axes[0,0].set_ylabel('Частота')

sns.histplot(sample_sports, ax=axes[0,1], kde=False, bins=25)
axes[0,1].set_title('Sports')
axes[0,1].set_xlabel('Оценка')
axes[0,1].set_ylabel('Частота')

# “s” - стандартизированная строка, ожидаемая статистика заказов масштабируется на стандартное
# отклонение данной выборки и к ним добавляется среднее значение
qqplot(sample_action, line='s', ax=axes[1,0])
qqplot(sample_sports, line='s', ax=axes[1,1])
plt.show()
In [50]:
alpha = 0.05 # критический уровень статистической значимости
            # если p-value окажется меньше него - отвергнем гипотезу

results_2 = st.ttest_ind(
    sample_action, 
    sample_sports,
    equal_var = False)

print('p-значение:', results_2.pvalue)

if (results_2.pvalue < alpha):
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
p-значение: 9.658176026869598e-15
Отвергаем нулевую гипотезу

Вывод по гипотеза № 2:
На имеющихся данных, на уровне значимости 5% (уровне доверия 95%) есть основания отвергнуть нулевую гипотезу о различии между средними значениями оценок пользователей жанров Action и Sports в пользу альтернативы. Таким образом, остается верным утверждение, что средние пользовательские рейтинги жанров Action и Sports отличаются.

Общий вывод¶

Шаг 1. Откройте файл с данными

  • Файл был открыт корректно

Шаг 2. Подготовьте данные

  • Ознакомились с данными, посмотрели на общую информацию о датасете, выявили пропуски и не соответствие типов данных.
  • На данном шаге мы заполнили некоторые пропуски, обнаружили необычное значени tbd - "to be determined" и заменили это значение на NaN.
  • Многие пропуски, например в столбцах оценок мы оставили незаполненными, чтобы не исказить статистику.
  • Также на данном этапе мы посчитали общее количество продаж по всем регионам и записали результат в столбец total_sales.

Шаг 3. Проведите исследовательский анализ данных

  • Было обнаружено, что рост выпуска игр приходится на 1994 год, а пик на 2008-2009 гг.
  • В среднем срок жизни платформы - 10 лет, также было решено оставить данные с 2012 по 2016 гг.
  • 3 потенциально прибыльных платформ - PS4, XOne, 3DS.
  • Наибольшие медианные продажи у платформ XOne и WiiU.
  • Почти у всех платформ есть определенные игры, которые "выстрелили".
  • Больше всего игр жанра Action, затем идут Shooter и Role-Playing.

Лучше всего покупают игры жанра Action, Shooter идёт на втором месте - и он самый выгодный, Role-Playing ожидаемо стоит на третьем месте по продажам.

  • Adventure сильно отстаёт по продажам, хотя игр выпускается много.

Шаг 4. Составьте портрет пользователя каждого региона

  • В NA самая популярная платформа PS4. Европейцы также предпочитают PS4. В Японии популярны 3DS.
  • В NA и EU самые популярные жанры практически совпадают. В JP вкусы отличаются.
  • В NA рейтинги M и T имеют практически одинаковую долю на рынке - по 38% и также рейтинги Е и Е10+ имеют практически одинаковую долю на рынке - по 14%. В EU все четрыре рейтинга распределенны практически равномерно - в среднем по 25%. В JP с учетом восточной психо-национальной особенности, лидером в рейтинге является T — «Подросткам» - 73.4%, рейтинг M - 21.5% и совсем малую часть занимают рейтинги Е и Е10+

Шаг 5. Проверьте гипотезы

  • Средние пользовательские рейтинги платформ Xbox One и PC одинаковые. Гипотеза не подтвердилась.
  • Средние пользовательские рейтинги жанров Action и Sports разные. Гипотеза подтвердилась!.
Дополнительные ссылки ❌

Подробнее: https://allatambov.github.io/psms/pdf/hypo-test.pdf и/или https://habr.com/en/company/uchi_ru/blog/500918/

In [ ]: